package com.itHuangTao.tank;

import com.almasb.fxgl.app.CursorInfo;
import com.almasb.fxgl.app.GameApplication;
import com.almasb.fxgl.app.GameSettings;
import com.almasb.fxgl.app.scene.*;
import com.almasb.fxgl.core.math.FXGLMath;
import com.almasb.fxgl.core.util.LazyValue;
import com.almasb.fxgl.dsl.FXGL;
import com.almasb.fxgl.entity.Entity;
import com.almasb.fxgl.time.TimerAction;
import com.itHuangTao.tank.collisions.*;
import com.itHuangTao.tank.components.TankComponent;
import com.itHuangTao.tank.ui.*;
import javafx.geometry.Point2D;
import javafx.geometry.Rectangle2D;
import javafx.scene.input.KeyCode;
import javafx.scene.paint.Color;
import javafx.util.Duration;

import java.util.List;
import java.util.Map;
import java.util.Random;

import static com.almasb.fxgl.dsl.FXGL.*;

public class TankApp extends GameApplication {

    public static int maxEnemyAmount;//敌方坦克的初始总数量
    public static int firstLevel = 1;

    public static int PropCount;//刚开始的道具数量
    public static int BombCountMax;//每一关最多只能出现炸弹的炸弹数量

    //设置敌人出现的间隔时间
    public static Duration enemyAppearance;
    //玩家变量
    private Entity player;
    //定时器行为
    private TimerAction freezeEnemyTimerAction;//冻结敌人
    private TimerAction reinforceTimerAction;//加固墙体
    private TimerAction spawnEnemyTimerAction;//显示敌人行为

    //将失败(胜利)场景变量存在一个集合当中
    private final LazyValue<FailedScene> failedSceneLazyValue = new LazyValue<>(FailedScene::new);
    private final LazyValue<SucceedScene> succeedSceneLazyValue = new LazyValue<>(SucceedScene::new);

    //游戏的初始化
    @Override
    protected void initGameVars(Map<String, Object> vars) {
        vars.put("freezeEnemy", false);//加入定时器
        vars.put("spawnedEnemyAmount", 0);//配置敌人的初始数量
        vars.put("destroyedEnemyAmount", 0);//消灭敌人的初始数量
        vars.put("level", firstLevel);//设置默认第一个的关卡
    }

    //重写游戏音乐初始化(实现循环播放)
    @Override
    protected void onPreInit() {
        loopBGM("gameBgm.wav");
    }

    //冻结敌人方法
    public void freezeEnemy() {
        expireTimerAction(freezeEnemyTimerAction);
        //加入定时器
        set("freezeEnemy", true);
        //重新执行定时器
        freezeEnemyTimerAction = runOnce(() -> set("freezeEnemy", false), Config.FREEZE_ENEMY_TIME);
    }

    //道具失效方法
    private void expireTimerAction(TimerAction freezeEnemyTimerAction) {
        //如果定时器正在使用(清理旧的定时器)
        if (freezeEnemyTimerAction != null && !freezeEnemyTimerAction.isExpired()) {
            //使得定时器失效
            freezeEnemyTimerAction.expire();
        }
    }

    //加固基地墙体的方法
    public void reinforce() {
        //如果定时器正在使用(清理旧的定时器)
        expireTimerAction(reinforceTimerAction);
        reinforceTimerAction = runOnce(() -> {
            //换回墙体为砖块
            updateFlagProtect("brick", "map/brick.png");
        }, Config.REINFORCE_TIME);
        //升级基地保护墙为石块
        updateFlagProtect("stone", "map/stone.png");
    }

    //不加固基地墙体的方法
    public void unReinforce() {
        for (Point2D point : Config.POINTS) {
            //获取加固点位置(并全部移除墙体)
            getGameWorld().getEntitiesAt(point).forEach(Entity::removeFromWorld);
        }
    }

    //更换加固基地墙体
    private static void updateFlagProtect(String brick, String assetName) {
        for (Point2D point : Config.POINTS) {
            //获取加固点位置
            getGameWorld().getEntitiesAt(point).forEach(Entity::removeFromWorld);
            Entity stone = spawn(brick, point);
            //给加固点换回砖块视图
            stone.getViewComponent().addChild(texture(assetName));
        }
    }

    //游戏界面基本的样式设置
    @Override
    protected void initSettings(GameSettings settings) {
        //设置默认的主菜单
        settings.setMainMenuEnabled(true);
        //设置标题
        settings.setTitle("Tank");
        //设置游戏的版本
        settings.setVersion("0.1");
        //设置游戏的宽高
        settings.setWidth(28 * Config.CELL_SIZE + 6 * Config.CELL_SIZE);
        settings.setHeight(28 * Config.CELL_SIZE);
        //设置按钮的样式
        settings.getCSSList().add("tank.css");
        //设置程序的图标
        settings.setAppIcon("icon.png");
        //设置鼠标样式
        settings.setDefaultCursor(
                new CursorInfo("cursor.png", 0, 0));
        //设置自定义背景图片
        settings.setSceneFactory(new SceneFactory() {
            //重写启动场景(不使用默认启动场景)
            @Override
            public StartupScene newStartup(int width, int height) {
                return new TankStartupScene(width, height);
            }

            //重写游戏进度条
            @Override
            public LoadingScene newLoadingScene() {
                return new TankLoadingScene();
            }

            //重写主菜单背景
            @Override
            public FXGLMenu newMainMenu() {
                return new TankMainMenu();
            }

        });
    }

    //游戏数据初始化(创建实体)
    @Override
    protected void initGame() {
        //前面做了一个静态导入（省略了写入代码区FXGL.的写法）(后面类的写法都省略了FXGL.)

        //设置游戏背景色：黑色
        getGameScene().setBackgroundColor(Color.BLACK);

        //指定创建游戏实体工厂类
        getGameWorld().addEntityFactory(new TankEntityFactory());

        //添加一个监听器判断游戏是否胜利
        Listener();
        //开始一个关卡
        startLevel();
    }

    //弹出游戏失败页面
    public void gameOver() {
        FXGL.getSceneService().pushSubScene(failedSceneLazyValue.get());
    }

    //添加一个监听器判断游戏是否胜利
    private void Listener() {
        //当前击毁坦克的数量达到最大值就判断胜利(添加监听器)
        getip("destroyedEnemyAmount").addListener((ob, ov, nv) -> {//object，oldV，newV
            if (nv.intValue() == maxEnemyAmount) {
                FXGL.runOnce(() -> {
                    //显示胜利界面
                    getSceneService().pushSubScene(succeedSceneLazyValue.get());
                }, Duration.seconds(1));//过了1秒后才显示胜利
            }
        });
    }

    //开始一个关卡
    public void startLevel() {
        //设置敌人产生的间隔时间
        enemyAppearanceTime();
        //重置数据
        expireTimerAction(spawnEnemyTimerAction);
        expireTimerAction(freezeEnemyTimerAction);
        expireTimerAction(reinforceTimerAction);
        set("freezeEnemy", false);
        set("spawnedEnemyAmount", 0);
        set("destroyedEnemyAmount", 0);
        firstLevel = 1;//默认是第一关
        PropCount = 0;//道具数量初始化
        maxEnemyAmount = 10;//敌人基数初始化
        BombCountMax = 2;//炸弹最大值初始化
        //随着关卡的增多增加敌人的最大数量
        maxEnemyAmount = maxEnemyAmount + FXGL.geti("level") * 5;
        //创建地图
        setLevelFromMap("level" + geti("level") + ".tmx");

        //创建玩家实体(并指定产生的位置)
        player = spawn("player", 219, 600);
        //游戏开始先创建三个坦克
        spawn("enemy", 30, 30);
        spawn("enemy", 330, 30);
        spawn("enemy", 630, 30);
        inc("spawnedEnemyAmount", 3);//用于记录敌方坦克的数量

        //创建敌方坦克实体（并指定产生的位置）
        //产生间隔
        spawnEnemyTimerAction = run(() -> {
            //获得敌方坦克的产生数量(达到最大值就不能产生)
            if (geti("spawnedEnemyAmount") == maxEnemyAmount) {
                expireTimerAction(spawnEnemyTimerAction);
                return;
            }
            //随机获取敌方坦克固定三个产生的位置
            Point2D p = FXGLMath.random(Config.SPAWN_ENEMY_POSITION).get();
            //在这个范围之内查找对象
            List<Entity> es = getGameWorld().getEntitiesInRange(new Rectangle2D(p.getX(), p.getY(), 39, 39));
            //在这个范围内出现以下的元素就不产生敌方坦克
            List<Entity> entities = es.stream().filter(entity ->
                    entity.isType(GameType.PLAYER)
                            || entity.isType(GameType.ENEMY)
                            || entity.isType(GameType.STONE)
                            || entity.isType(GameType.SEA)).toList();
            if (entities.isEmpty()) {
                //绘画并创建坦克
                spawn("enemy", p);
                //每产生一次坦克就数量加一
                inc("spawnedEnemyAmount", 1);
            }
        }, enemyAppearance);//坦克产生的时间间隔

        //新加入一个信息界面
        GameView view = new GameView(new InfoPane(), Integer.MAX_VALUE);
        getGameScene().addGameView(view);
    }

    //敌人产生间隔时间的方法
    private void enemyAppearanceTime() {
        Random r = new Random();
        if (FXGL.geti("level") <= 4) {
            //设置间隔时间为1-2秒（随机数决定）
            int time = r.nextInt(1) + 1;
            enemyAppearance = Duration.seconds(time);
        } else {
            //设置间隔时间为1-3秒（随机数决定）
            int time = r.nextInt(2) + 1;
            enemyAppearance = Duration.seconds(time);
        }
    }

    //物理碰撞
    @Override
    protected void initPhysics() {
        //获得物理世界并添加一个碰撞处理器

        //敌人和子弹碰撞
        getPhysicsWorld().addCollisionHandler(new BulletEnemyHandler());
        //玩家和子弹碰撞
        getPhysicsWorld().addCollisionHandler(new BulletPlayerHandler());
        //我方子弹和敌方子弹的碰撞
        getPhysicsWorld().addCollisionHandler(new BulletBulletHandler());
        //子弹与旗帜/基地的碰撞
        getPhysicsWorld().addCollisionHandler(new BulletFlagHandler());

        //子弹和地形的碰撞
        BulletBrickHandler bulletBrickHandler = new BulletBrickHandler();
        //1.土墙和子弹碰撞
        getPhysicsWorld().addCollisionHandler(bulletBrickHandler);
        //2.石头和子弹碰撞
        getPhysicsWorld().addCollisionHandler(bulletBrickHandler.copyFor(GameType.BULLET, GameType.STONE));
        //3.森林和子弹碰撞
        getPhysicsWorld().addCollisionHandler(bulletBrickHandler.copyFor(GameType.BULLET, GameType.GREENS));

        //4.海面和玩家碰撞
        getPhysicsWorld().addCollisionHandler(new SeaPlayerHandler());
        //5.海面和敌人碰撞
        getPhysicsWorld().addCollisionHandler(new SeaEnemyHandler());

        //边界和子弹碰撞
        getPhysicsWorld().addCollisionHandler(new BulletBorderHandler());

        //玩家和道具碰撞
        getPhysicsWorld().addCollisionHandler(new ItemPlayerHandler());
        //敌人和道具碰撞
        getPhysicsWorld().addCollisionHandler(new ItemEnemyHandler());
    }

    //实现上下左右移动的方法(接受键盘的信息)
    @Override
    protected void initInput() {
        //上移-->
        onKey(KeyCode.UP, () -> {
            if (playerIsActive()) {
                TankComponent playerComponent = player.getComponent(TankComponent.class);
                //取出上移的组件（自定义）
                playerComponent.moveUp();
            }
        });
        //下移-->
        onKey(KeyCode.DOWN, () -> {
            if (playerIsActive()) {
                TankComponent playerComponent = player.getComponent(TankComponent.class);
                //取出下移的组件（自定义）
                playerComponent.moveDown();
            }
        });
        //左移-->
        onKey(KeyCode.LEFT, () -> {
            if (playerIsActive()) {
                TankComponent playerComponent = player.getComponent(TankComponent.class);
                //取出左移的组件（自定义）
                playerComponent.moveLeft();
            }
        });
        //右移-->
        onKey(KeyCode.RIGHT, () -> {
            if (playerIsActive()) {
                TankComponent playerComponent = player.getComponent(TankComponent.class);
                //取出右移的组件（自定义）
                playerComponent.moveRight();
            }
        });
        //发射-->
        onKey(KeyCode.SPACE, () -> {
            if (playerIsActive()) {
                TankComponent playerComponent = player.getComponent(TankComponent.class);
                //取出射击的组件（自定义）
                playerComponent.shoot();
            }
        });
    }

    //判断玩家是否存活
    public boolean playerIsActive() {
        return player != null && player.isActive();
    }

    public static void main(String[] args) {
        //创建游戏窗口
        launch(args);
    }
}
